// // Copyright (c) 2009 All Right Reserved // // vl // // 2009-02-01 // using System; using System.Collections; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Diagnostics.Contracts; using System.Globalization; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Xml.Linq; using System.Xml.Serialization; using JetBrains.Annotations; using LargoCommon.Abstract; using LargoCommon.Interfaces; namespace LargoCommon.Music { /// /// Musical Bar. /// public class MusicalBar : IAbstractBar { #region Fields /// Cluster level for tick. private readonly Dictionary clusterLevelForTick; /// /// List of harmonic clusters. /// private List harmonicClusters; /// /// The elements. /// private List elements; /// /// The count of tones /// private int countOfTones; /// /// The value of melodic ticks /// private int valueOfMelodicTicks; /// /// The value of rhythmic ticks /// private int valueOfRhythmicTicks; #endregion #region Constructors /// /// Initializes a new instance of the class. /// public MusicalBar() { this.clusterLevelForTick = new Dictionary(); this.elements = new List(); this.TempoNumber = 120; this.HarmonicBar = new HarmonicBar(0, 0); } /// /// Initializes a new instance of the MusicalBar class. Serializable. /// /// The bar number. /// The body. public MusicalBar(int barNumber, MusicalBody body) : this() { this.Body = body; this.BarNumber = barNumber; } /// Initializes a new instance of the class. /// The given musical block. /// The given code. /// Length of the given. public MusicalBar(MusicalBlock givenBlock, string givenCode, int givenLength) : this() { this.Body = givenBlock.Body; this.elements = new List(); this.IsEmpty = string.IsNullOrEmpty(givenCode); this.SystemLength = givenLength; this.StructuralCode = givenCode; var chord = new HarmonicStructure(this.Body.Context.Header.System.HarmonicSystem, givenCode); //// HarmonicSystem.GetHarmonicSystem(DefaultValue.HarmonicOrder) this.HarmonicBar.SetStructure(chord); } /// /// Initializes a new instance of the class. /// /// The given musical block. /// The mark bar. /// Thrown when a method Contract has been broken. public MusicalBar(MusicalBlock givenBlock, XElement markBar) { Contract.Requires(markBar != null); if (markBar == null) { return; } this.Body = givenBlock.Body; this.elements = new List(); var xcell = markBar.Element("Chord"); this.SystemLength = XmlSupport.ReadIntegerAttribute(markBar.Attribute("Length")); this.IsEmpty = XmlSupport.ReadBooleanAttribute(markBar.Attribute("IsEmpty")); if (xcell != null) { this.StructuralCode = XmlSupport.ReadStringAttribute(xcell.Attribute("Code")); } this.HarmonicStructure = new HarmonicStructure(givenBlock.Header.System.HarmonicSystem, this.StructuralCode); //// HarmonicSystem.GetHarmonicSystem(DefaultValue.HarmonicOrder) //// this.HarmonicPotential = XmlSupport.ReadByteAttribute(markCell.Attribute("HarmonicPotential")); var xelements = markBar.Elements("Element"); foreach (var xe in xelements) { MusicalElement element = new MusicalElement(xe, this.Header); int barNumber = XmlSupport.ReadIntegerAttribute(xe.Attribute("Bar")); int lineIndex = XmlSupport.ReadIntegerAttribute(xe.Attribute("LineIndex")); var line = givenBlock.ContentLines[lineIndex]; element.Bar = this; element.Line = line; this.Elements.Add(element); } } #endregion #region Properties - Xml /// Gets Xml representation. /// Property description. public virtual XElement GetXElement { get { XElement xbar = new XElement("Bar"); xbar.Add(new XAttribute("Number", this.BarNumber)); //// Lines XElement xelements = new XElement("Elements"); //// Musical Element to XML foreach (MusicalElement element in this.Elements.Where(element => element != null)) { //// mtrack.MusicalBlock = givenMusicalBlock; var xelement = element.GetXElement; xelements.Add(xelement); } xbar.Add(xelements); return xbar; } } #endregion #region Properties - Harmonic Status /// /// Gets Harmonic Status. /// /// Property description. public HarmonicChange HarmonicStatus { get; private set; } /// /// Gets a value indicating whether this instance has harmonic motive. /// /// /// Is true if this instance has harmonic motive; otherwise, false. /// public bool HasHarmonicMotive { get { if (this.HarmonicStatus == null) { return false; } var hm = this.HarmonicStatus.HarmonicMotive; return hm?.HarmonicStream.HarmonicBars != null && hm.HarmonicStream.HarmonicBars.Any(); } } /// /// Gets or sets current harmonical bar. /// /// /// Property description. /// [XmlIgnore] public HarmonicBar HarmonicBar { get; set; } /// Gets or sets the harmonic structure. /// The harmonic structure. public HarmonicStructure HarmonicStructure { get => this.HarmonicBar?.HarmonicStructures?.FirstOrDefault(); set => this.HarmonicBar.SetStructure(value); } #endregion #region Properties - Tempo Status /// Gets or sets class of melodic part. /// Property description. public int TempoNumber { get; set; } #endregion #region Properties /// /// Gets or sets the body. /// /// /// The body. /// public MusicalBody Body { get; set; } /// /// Gets the header. /// /// /// The header. /// public MusicalHeader Header => this.Body.Context.Header; /// Gets or sets the zero-based index of the bar. /// The bar index. public int BarIndex { get; set; } /// Gets or sets number o bar. /// Property description. [XmlAttribute] public int BarNumber { get => this.BarIndex + 1; set => this.BarIndex = value - 1; } /// Gets or sets the system length. /// The length of the system. public int SystemLength { get; set; } /// Gets or sets the structural code. /// The structural code. public string StructuralCode { get; set; } //// public byte HarmonicPotential { get; set; } /// Gets or sets a value indicating whether this object is empty. /// True if this object is empty, false if not. public bool IsEmpty { get => (this.HarmonicBar == null) || this.HarmonicBar.IsEmpty; set { //// this.Status.HarmonicBar.IsEmpty = value; } } /// /// Gets the elements. /// /// /// The elements. /// public IList Elements => this.elements; /// /// Gets the rhythmic order divisor. /// /// /// The rhythmic order divisor. /// public byte RhythmicOrderDivisor { get { var factor = 0; //// rstruct.GSystem.Order; foreach (var elem in this.elements) { if (elem.Status.RhythmicStructure == null || elem.Status.RhythmicStructure.Level == 0) { continue; } var header = this.Body.Context.Header; var rhythmicShape = new RhythmicShape(header.System.RhythmicOrder, elem.Status.RhythmicStructure); var listDistances = rhythmicShape.BitDistances; foreach (byte d in listDistances) { if (factor == 0) { factor = d; } else { factor = (int)MathSupport.GreatestCommonDivisor(factor, d); } } } return (byte)(factor == 0 ? 1 : factor); } } /// Gets or sets time tag. /// In ticks according to order of rhythmical system.. [XmlIgnore] public int TimePoint { get; set; } /// Gets or sets common rhythmical shape. /// Property description. public RhythmicShape CommonRhythmicShape { get; set; } #endregion #region Internal Properties /// Gets list of harmonic clusters. /// Property description. [XmlIgnore] public Collection HarmonicClusters { get { Contract.Ensures(Contract.Result>() != null); if (this.harmonicClusters == null) { this.harmonicClusters = new List(); } /* 2019/09 if (this.harmonicClusters == null) { throw new InvalidOperationException("List of clusters is null."); } */ return new Collection(this.harmonicClusters); } } /// /// Gets or sets the previous bar. /// /// /// Property description. /// [XmlIgnore] public MusicalBar PreviousBar { get; set; } /// Gets or sets the previous bar. /// Property description. /// Returns value. [XmlIgnore] public MusicalBar NextBar { get; set; } #endregion #region Tone Properties /// /// Gets the tones. /// /// /// The tones. /// public IList Tones { get { var ts = new List(); foreach (var elem in this.elements) { ts.AddRange(elem.Tones); } return ts; } } /// /// Gets the count of tones. /// /// /// The count of tones. /// [UsedImplicitly] public int CountOfTones { get { if (this.countOfTones > 0) { return this.countOfTones; } this.countOfTones = this.Tones.Count; return this.countOfTones; } } /// /// Gets the melodic tones. /// /// /// The melodic tones. /// public IList MelodicTones { get { var ts = new List(); // ReSharper disable once LoopCanBeConvertedToQuery foreach (var elem in this.elements) { // ReSharper disable once LoopCanBeConvertedToQuery foreach (var t in elem.Tones) { if (t is MusicalTone mt && mt.IsTrueTone) { ts.Add(mt); } } } return ts; } } #endregion #region Relation to harmonic motive /// Gets or sets a value indicating whether is block nested. /// Is bar nested from other block. public int NumberInHarmonicMotive { get; set; } /// /// Gets or sets a value indicating whether [last in harmonic motive]. /// /// /// True if [is last in harmonic motive]; otherwise, false. /// public bool IsLastInHarmonicMotive { get; set; } #endregion #region ToString properties /// Gets Write particular structures to string. /// Returns value. /// General musical property. public string ChordsToString => this.HarmonicBar.ChordsToString; /// Gets Write particular structures to string. /// Returns value. /// General musical property. public string PureChordsToString => this.HarmonicBar.PureChordsToString; /// Gets String representation of clusters. /// Property description. /// Returns value. public string ClustersToString { get { if (this.CommonRhythmicShape == null) { return string.Empty; } var rhyShape = this.CommonRhythmicShape; var s = new StringBuilder(); //// s.Append("Clusters:"); //// +"\n" var shapeLevel = this.CommonRhythmicShape.Level; for (byte level = 0; level < shapeLevel; level++) { var hc = this.harmonicClusters != null && level < this.HarmonicClusters.Count ? this.HarmonicClusterAtLevel(level) : null; if (hc == null) { continue; } if (level == 0) { s.AppendFormat(CultureInfo.CurrentCulture, "{0,4}:\n", this.BarNumber.ToString("D", CultureInfo.CurrentCulture.NumberFormat)); } else { s.Append(" "); } //// Cluster s.AppendFormat(CultureInfo.CurrentCulture, "{0,16}", hc); //// {0,12} s.Append(string.Format(CultureInfo.CurrentCulture, "({0} ticks) ", rhyShape.DistanceAtLevel(level).ToString("D", CultureInfo.CurrentCulture.NumberFormat))); s.Append("\t\t "); if (hc.HarmonicStructure != null) { s.AppendFormat(CultureInfo.CurrentCulture, hc.HarmonicStructure.ToneSchema.PadRight(15), CultureInfo.CurrentCulture); if (hc.HarmonicStructure.HarmonicModality != null) { s.Append(string.Format(CultureInfo.CurrentCulture, " ({0})", hc.HarmonicStructure.HarmonicModality.ToneSchema)); //// ???? } } s.Append("\n"); } return s.ToString().Trim(); } } #endregion /// Gets a list of identifiers. /// A list of identifiers. public IList Identifiers { get { var items = new List(); string s; var item = new KeyValuePair("Bar number", this.BarNumber.ToString()); items.Add(item); item = new KeyValuePair("Time point", this.TimePoint.ToString()); items.Add(item); item = new KeyValuePair("Harmonic modality", this.HarmonicBar?.HarmonicModality?.ToString()); items.Add(item); item = new KeyValuePair("Structures", this.HarmonicBar?.SimpleStructuralOutline); items.Add(item); item = new KeyValuePair("Structures in detail", this.HarmonicBar?.StructuralOutline); items.Add(item); var rhythmicOrder = this.HarmonicBar?.Header?.System?.RhythmicOrder; //// Body.Context.Header.System.RhythmicOrder; if (rhythmicOrder != null) { var barBitRange = new BitRange((byte)rhythmicOrder, 0, (byte)rhythmicOrder); var harmonicStructure = this.HarmonicBar.PrevailingHarmonicStructure(barBitRange, out var simpleHarmony); item = new KeyValuePair("Prevailing structure", harmonicStructure?.ToneSchema); items.Add(item); item = new KeyValuePair("Simple harmony", simpleHarmony.ToString()); items.Add(item); s = string.Format(CultureInfo.CurrentCulture.NumberFormat, "{0,6:F2} ", this.HarmonicBar?.MeanRhythmicMobility); item = new KeyValuePair("Mean rhythmic mobility", s); items.Add(item); s = string.Format(CultureInfo.CurrentCulture.NumberFormat, "{0,6:F2} ", this.HarmonicBar?.MeanRhythmicTension); item = new KeyValuePair("Mean rhythmic tension", s); items.Add(item); } s = string.Format(CultureInfo.CurrentCulture.NumberFormat, "{0,6:F2} ", this.HarmonicBar?.MeanConsonance); item = new KeyValuePair("Mean consonance", s); items.Add(item); s = string.Format(CultureInfo.CurrentCulture.NumberFormat, "{0,6:F2} ", this.HarmonicBar?.MeanContinuity); item = new KeyValuePair("Mean continuity", s); items.Add(item); s = string.Format(CultureInfo.CurrentCulture.NumberFormat, "{0,6:F2} ", this.HarmonicBar?.MeanImpulse); item = new KeyValuePair("Mean impulse", s); items.Add(item); s = string.Format(CultureInfo.CurrentCulture.NumberFormat, "{0,6:F2} ", this.HarmonicBar?.MeanPotential); item = new KeyValuePair("Mean potential", s); items.Add(item); s = string.Format(CultureInfo.CurrentCulture.NumberFormat, "{0,6:F2} ", this.TempoNumber); item = new KeyValuePair("Tempo number", this.TempoNumber.ToString()); items.Add(item); return items; } } #region Public methods /// /// Elements the of line. /// /// The track identifier. /// Returns value. public MusicalElement ElementOfLine(Guid lineIdent) { var element = (from v in this.Elements where v.Line.LineIdent == lineIdent select v).FirstOrDefault(); //// var selectedList = (from element in list orderby element.Point.BarNumber //// select element).ToList(); return element; } /// /// Counts the of ticks. /// /// if set to true [break for rests]. /// Type of the line. /// /// Returns value. /// public int CountOfSelectedTicks(bool breakForRests, MusicalLineType lineType) { var rhythmicOrder = this.Body.Context.Header.System.RhythmicOrder; var bitArray = new BitArray(rhythmicOrder); //// for (byte tick = 0; tick < rhythmicOrder; tick++) { foreach (var elem in this.Elements) { if (!elem.MusicalLine.IsSelected || elem.MusicalLine.FirstStatus.LineType != lineType) { continue; } if (elem.Status.RhythmicStructure == null) { continue; } //// var binStruct = elem.Status.RhythmicStructure.BinaryStructure(breakForRests); //// bitArray = bitArray.Or(binStruct.BitArray); var binStructArray = elem.Status.RhythmicStructure.BitArray(breakForRests); bitArray = bitArray.Or(binStructArray); } byte sum = 0; foreach (var bit in bitArray) { sum += (byte)(((bool)bit) ? 1 : 0); } return sum; } /// /// Resets the values of ticks. /// public void ResetValuesOfTicks() { this.valueOfMelodicTicks = 0; this.valueOfRhythmicTicks = 0; } #endregion #region Public methods #region Apply Changes /// /// Applies the harmonic change. /// /// The harmonic change. public void ApplyHarmonicChange(HarmonicChange harmonicChange) { //// Contract.Requires(harmonicChange != null); if (harmonicChange == null) { return; } this.HarmonicStatus = harmonicChange; } /// /// Applies the tempo change. /// /// The tempo change. public void ApplyTempoChange(TempoChange tempoChange) { //// Contract.Requires(harmonicChange != null); if (tempoChange == null) { return; } this.TempoNumber = tempoChange.TempoNumber; } #endregion /* Clone bar - Not needed, not used. /// Makes a deep copy of the MusicalBarStatus object. /// Returns object. public object Clone() { var tmc = new BarStatus(this.BarNumber) { HarmonicStatus = this.HarmonicStatus, TempoNumber = this.TempoNumber }; if (this.HarmonicBar != null) { tmc.HarmonicBar = (HarmonicBar)this.HarmonicBar.Clone(); } return tmc; } */ /// /// Sets the T harmonic motive bar. /// /// The value. public void SetHarmonicBar(HarmonicBar value) { if (value == null) { throw new ArgumentException("Empty harmonic bar!"); } if (value.Header == null) { value.Header = this.Body.Context.Header; } this.HarmonicBar = (HarmonicBar)value.Clone(); //// value if (this.HarmonicBar.IsEmpty) { this.HarmonicBar.CheckStructures(); } var rhythmicOrder = this.HarmonicBar.Header.System.RhythmicOrder; var metre = RhythmicSystem.GetRhythmicSystem(RhythmicDegree.Shape, rhythmicOrder); var metricStructuralCode = this.HarmonicBar.GetBarMetricCode; this.HarmonicBar.RhythmicShape = RhythmicShape.GetNewRhythmicShape(metre, metricStructuralCode); //// Have to be clone of bar above ?!?!?!? //// else { this.HarmonicBar.RegenerateStructures(); } } /// /// Gets the element. /// /// The line identifier. /// Returns value. public MusicalElement GetElement(Guid lineIdent) { var element = (from elem in this.elements where elem.Line.LineIdent == lineIdent select elem).FirstOrDefault(); return element; } /// Makes a deep copy of the MusicalPitch object. /// Returns object. public object Clone() { var bar = new MusicalBar(this.BarNumber, this.Body) { TimePoint = this.TimePoint, CommonRhythmicShape = this.CommonRhythmicShape }; foreach (var element in this.Elements) { var newElement = (MusicalElement)element.Clone(); newElement.Bar = bar; bar.Elements.Add(newElement); } bar.MakeHarmonicClusters(); return bar; } /// /// Sets the modality to structures. /// /// The minimum modality level. public void SetModalityToStructures(byte minModalityLevel) { foreach (var harStr in this.HarmonicBar.HarmonicStructures.Where(harStr => this.Body.Bars != null && this.Body != null)) { harStr.HarmonicModality = this.HarmonicModalityFromStructures(minModalityLevel); } } /// /// Returns harmonic modality from all harmonic structures. /// /// The minimum modality level. /// /// Returns value. /// public HarmonicModality HarmonicModalityFromStructures(byte minModalityLevel) { if (this.HarmonicBar?.HarmonicStructures == null || this.HarmonicBar?.HarmonicStructures?.Count <= 0) { return null; } var bits = this.BitsOccupiedByHarmony(minModalityLevel); var firstStructure = this.HarmonicBar.HarmonicStructures[0]; var modality = firstStructure != null ? HarmonicModality.GetNewHarmonicModality(firstStructure.HarmonicSystem, bits) : null; return modality; } /// /// Sets the elements. /// /// The given elements. public void SetElements(IList givenElements) { this.elements = (List)givenElements; } /// /// Writes musical block to Scores - Orchestration. /// /// /// Returns value. /// public OrchestraStrip OrchestraStrip() { var strip = new OrchestraStrip(); //// Cycle through all the sequence //// byte number = 0; this.elements.ForEach(mt => { if (mt != null) { //// && !mt.IsEmpty && mt.IsSelected var barTones = mt.SingleMelodicTones(); if (barTones.Count > 0) { //// MelodicFunction melodicType = barTones.DetermineMelodicType(mbar.HarmonicBar); //// MusicalLoudness loudness = barTones.MeanLoudness; var mstatus = mt.Status; if (mstatus == null) { return; } //// var instrument = new GeneralChannel(InstrumentGenus.Melodical, mstatus.Instrument, mstatus.Channel); if (!mstatus.Instrument.IsEmpty) { //// 2019/10 var track = new OrchestraTrack(barTones.MeanOctave, mstatus.Instrument); strip.OrchestraTracks.Add(track); } //// number++; } else { var barStrikes = mt.Tones; if (barStrikes.Count > 0) { //// MelodicFunction melodicType = barTones.DetermineMelodicType(mbar.HarmonicBar); //// MusicalLoudness loudness = barTones.MeanLoudness; var mstatus = mt.Status; if (mstatus == null) { return; } //// var instrument = new GeneralChannel(InstrumentGenus.Melodical, mstatus.Instrument, mstatus.Channel); if (!mstatus.Instrument.IsEmpty) { //// 2019/10 var track = new OrchestraTrack(mstatus.Instrument); strip.OrchestraTracks.Add(track); } //// number++; } } } }); strip.RecomputeProperties(); return strip; } /// /// Swaps the harmonic with. /// /// The given bar. public void SwapHarmonyWith(MusicalBar givenBar) { Contract.Requires(givenBar != null); var harmonicBar = givenBar.HarmonicBar; var newHarmonicBar = (HarmonicBar)harmonicBar.Clone(); newHarmonicBar.BarNumber = givenBar.BarNumber; givenBar.SetHarmonicBar(newHarmonicBar); this.SetHarmonicBar(harmonicBar); } /// /// Melodic tones around. /// /// The bar numbers back. /// The bar numbers forward. /// /// Returns value. /// public IEnumerable MelodicTonesAround(int barNumbersBack, int barNumbersForward) { var ts = new List(); foreach (var elem in this.Elements) { ts.AddRange(elem.MelodicTonesAround(barNumbersBack, barNumbersForward)); } return ts; } /// /// Surroundings the bars. /// /// The bars before. /// The bars after. /// Returns value. public IList SurroundingBars(byte barsBefore, byte barsAfter) { var musicalBars = new List(); var currentBar = this; musicalBars.Add(currentBar); for (byte i = 0; i <= barsBefore; i++) { if (currentBar == null) { break; } currentBar = currentBar.PreviousBar; if (currentBar != null) { musicalBars.Add(currentBar); } } currentBar = this; for (byte i = 0; i <= barsAfter; i++) { if (currentBar == null) { break; } currentBar = currentBar.NextBar; if (currentBar != null) { musicalBars.Add(currentBar); } } return musicalBars; } /// /// Compose rhythm of lines in given bar. /// public void FillTracksWithRhythm() { //// Ordering is not needed here, motives are loaded from status //// All - not only (&& mt.Status != null) if (this.Body.Context.Settings.ParallelMode) { Parallel.ForEach( this.Elements, mt => { if (mt == null) { return; } mt.FillWithRhythm(); }); } else { this.elements.ForEach(mt => { if (mt == null) { return; } mt.FillWithRhythm(); }); } } /// /// Transfers the status to tones. /// public void SendStatusToTones() { foreach (var element in this.Elements) { if (element?.Status == null) { continue; } var status = element.Status; foreach (var mt in element.Tones) { if (!(mt is MusicalStrike mtone)) { continue; } mtone.InstrumentNumber = status.Instrument.Number; mtone.Staff = status.Staff; mtone.Loudness = status.Loudness; } } } /// /// Eliminates the parallel melodies. /// Correct melodic type - eliminate parallel individualized melodic voices. /// public void EliminateParallelMelodies() { var ems = this.Elements; var hasMelody = false; foreach (var me in ems) { if (me == null || me.Line.Purpose != LinePurpose.Composed || me.Status == null) { continue; } var mstatus = me.Status; if (mstatus == null) { continue; } var melFilling = mstatus.MelodicFunction == MelodicFunction.MelodicFilling; var melMotion = mstatus.MelodicFunction == MelodicFunction.MelodicMotion; if (hasMelody) { if (melFilling) { mstatus.MelodicFunction = MelodicFunction.HarmonicFilling; } else { if (melMotion) { mstatus.MelodicFunction = MelodicFunction.HarmonicMotion; } } } else { hasMelody = melFilling || melMotion; } } } #endregion #region Planned Tones /// /// Prepare Planned Tones. /// public void PreparePlannedTones() { //// var hs = this.Body.Context.Header.System.HarmonicSystem; var hm = this.HarmonicModalityFromStructures(MusicalSettings.Singleton.MinimalModalityLevel); ////HarmonicModality hm = MusicalTrackEngine.DetermineHarmonicModality(musicalBar, null, HarmonicModalizationType.Consecutive); if (hm == null || hm.Level <= 1) { //// 2016/07 return; } foreach (var elem in this.Elements) { if (elem == null || elem.Status?.LocalPurpose != LinePurpose.Composed) { continue; } //// bool figural = mtrack.CurrentMelodicStructure != null; //// mp.Status.HasMelodicMotive //// if ((this.Status == null) || !figural) { continue; } var tones = elem.Tones; if (tones != null) { /*bool fromMelodicStructure = false; if (fromMelodicStructure) { elem.ProjectIntoPlannedTones(hs, hm, tones); } else { elem.Status.PlannedTones = tones; }*/ } } } #endregion #region Public Clusters /// /// Makes common rhythmical shape, i.e. elementary rhythm from all parts. /// /// Returns value. public bool MakeCommonRhythmicShape() { this.CommonRhythmicShape = this.DetermineCommonRhythmicShape(true); //// , this.BarNumber if (this.CommonRhythmicShape == null) { return false; } this.AssignClusterLevelsForTicks(); return true; } /// /// Add harmonic cluster to the list of clusters. /// /// Given Cluster. public void AddHarmonicCluster(HarmonicCluster givenCluster) { Contract.Requires(givenCluster != null); this.HarmonicClusters.Add(givenCluster); givenCluster.BarNumber = this.BarNumber; } /// /// Makes harmonic clusters for this bar. /// public void MakeHarmonicClusters() { //// byte rhythmicOrder, IEnumerable givenMusicalTracks //// Contract.Requires(givenMusicalTracks != null); //// var musicalTracks = givenMusicalLines as IList ?? givenMusicalTracks.ToList(); if (!this.MakeCommonRhythmicShape()) { //// this.Number, true //// rhythmicOrder, musicalTracks return; } this.harmonicClusters = new List(); if (this.CommonRhythmicShape == null) { return; } var level = this.CommonRhythmicShape.Level; for (byte lev = 0; lev < level; lev++) { if (this.CommonRhythmicShape == null) { continue; } var hc = this.MakeHarmonicClusterAtLevel(lev); if (hc == null) { continue; } this.AddHarmonicCluster(hc); } } /// Re-compute properties of harmonic clusters in this bar. /// Given range. public void RecomputeHarmonicClusters(BitRange range) { Contract.Requires(this.CommonRhythmicShape != null); if (range == null) { return; } //// byte lastLevel = 127; var rhyShape = this.CommonRhythmicShape; var levelFrom = rhyShape.LevelOfBit(range.BitFrom); var levelTo = rhyShape.LevelOfBit(range.BitTo); for (var level = levelFrom; level <= levelTo; level++) { if (level >= this.HarmonicClusters.Count) { break; } var harCluster = this.HarmonicClusters[level]; harCluster?.Recompute(); } } /// Returns harmonic clusters at given place. /// Requested level. /// Returns value. public HarmonicCluster HarmonicClusterAtLevel(byte givenLevel) { Contract.Requires(givenLevel < this.HarmonicClusters.Count); if (this.HarmonicClusters == null || this.HarmonicClusters.Count == 0 || givenLevel >= this.HarmonicClusters.Count) { return null; } var harCluster = this.HarmonicClusters[givenLevel]; return harCluster; } /// Returns harmonic clusters at given tick. /// Requested tick. /// Returns value. public HarmonicCluster HarmonicClusterAtTick(byte givenTick) { var rhyShape = this.CommonRhythmicShape; if (rhyShape == null) { return null; } var level = this.clusterLevelForTick[givenTick]; //// this.CommonRhythmicShape.LevelOfBit(givenTick); if (level >= this.HarmonicClusters.Count) { return null; } var harCluster = this.HarmonicClusters[level]; return harCluster; } #endregion #region Common Rhythmic Shape /// /// Common Rhythmic Shape. /// /// If set to true [consider current status]. /// /// Returns value. /// /// Incompatible rhythmical orders. public RhythmicShape DetermineCommonRhythmicShape(bool considerTrackStatus) { var rhythmicOrder = this.Body.Context.Header.System.RhythmicOrder; //// in variable "bits" is assembled the common rhythmic shape var bits = new BitArray(rhythmicOrder); bits.SetAll(false); //// .Where(mt => mt != null && mt.IsSelected) foreach (var mt in this.Elements.Where(mt => mt.Status?.LineType == MusicalLineType.Melodic)) { if (considerTrackStatus) { if (mt.Status.RhythmicStructure == null) { var tones = mt.Tones; ////Track.MusicalTonesInBar(givenBarNumber); if (tones != null) { var rstruct = new RhythmicStructure(rhythmicOrder, tones); mt.Status.RhythmicStructure = rstruct; } } if (mt.Status.RhythmicStructure == null) { continue; } } var binStr = mt.Status.RhythmicStructure.BinaryStructure(false); //// 2015/01 if (binStr == null) { continue; } if (binStr.BitArray.Length == bits.Length) { //// 2012/06 //// null test not needed (resharper) bits = bits.Or(binStr.BitArray); } else { throw new InvalidOperationException("Incompatible rhythmical orders."); } } var commonRhythmicShape = RhythmicShape.GetNewRhythmicShape(this.Body.Context.Header.System.RhythmicSystem, bits); return commonRhythmicShape; } /// /// Common Rhythmic Structure. /// /// /// Returns value. /// public RhythmicStructure CommonRhythmicStructure() { Contract.Requires(this.Elements != null); var rhythmicOrder = this.Body.Context.Header.System.RhythmicOrder; //// in variable "elements" is assembled the common rhythmic shape var list = new short[rhythmicOrder]; //// each of elements = 0 foreach (var mt in this.Elements.Where(mt => mt.Status.LineType == MusicalLineType.Melodic)) { var tones = mt.Tones; //// Track.MusicalTonesInBar(givenBarNumber); if (tones != null) { var rhyStr = new RhythmicStructure(rhythmicOrder, tones); for (byte tick = 0; tick < rhythmicOrder; tick++) { if (rhyStr.ElementList == null || tick > rhyStr.ElementList.Count - 1) { //// 2014/01 break; } var elem = rhyStr.ElementList[tick]; if (list[tick] == 0 && elem > 0) { list[tick] = elem; } } } } var rs = new RhythmicStructure { GSystem = RhythmicSystem.GetRhythmicSystem(RhythmicDegree.Structure, rhythmicOrder) }; rs.SetElementList(new Collection(list)); return rs; } /// /// Gets the value of ticks. /// /// Type of the line. /// Returns value. /// /// The value of ticks. /// public int ValueOfTicks(MusicalLineType lineType) { if (lineType == MusicalLineType.Melodic) { if (this.valueOfMelodicTicks > 0) { return this.valueOfMelodicTicks; } } if (lineType == MusicalLineType.Rhythmic) { if (this.valueOfRhythmicTicks > 0) { return this.valueOfRhythmicTicks; } } var value1 = this.CountOfSelectedTicks(false, lineType); //// var value2 = this.CountOfSelectedTicks(true, lineType); //// int value = (2 * value1) + value2 + this.CountOfTones; return value1; } #endregion #region String representation /// String representation of the object. /// Returns value. public override string ToString() { var s = new StringBuilder(); s.AppendFormat(CultureInfo.CurrentCulture, "Bar n.{0,3}: ", this.BarNumber); //\r\n s.Append(this.HarmonicBar); s.Append(this.ChordsToString); s.Append(this.ClustersToString); return s.ToString(); } /// String representation of the object. /// Returns value. public string NumberToString() { var s = new StringBuilder(); s.AppendFormat(CultureInfo.CurrentCulture, "{0,3}: ", this.BarNumber); return s.ToString(); } #endregion #region Private methods /// /// Makes harmonic cluster for a given rhythmical place. /// /// Requested level. /// /// Returns value. /// private HarmonicCluster MakeHarmonicClusterAtLevel(byte givenLevel) { //// Contract.Requires(musicalTracks != null); Contract.Requires(this.CommonRhythmicShape != null); if (this.HarmonicBar == null) { return null; } var tick = this.CommonRhythmicShape.PlaceAtLevel(givenLevel); var duration = this.CommonRhythmicShape.DistanceAtLevel(givenLevel); var harmonicStructure = this.HarmonicBar.HarmonicStructureAtTick(tick); var harCluster = new HarmonicCluster(harmonicStructure, tick, duration); foreach (var mt in from elem in this.Elements where elem?.Tones != null //// && mtrack.IsSelected where elem.Status?.LineType == MusicalLineType.Melodic select elem.MusicalToneAt(tick, elem.Status.RhythmicStructure) as MusicalTone into mt where mt != null && mt.ToneType == MusicalToneType.Melodic select mt) { harCluster.AddMelodicTone(mt); } harCluster.Recompute(); return harCluster; } /// /// Assign Cluster Levels For Ticks. /// private void AssignClusterLevelsForTicks() { Contract.Requires(this.CommonRhythmicShape != null); var rhyShape = this.CommonRhythmicShape; for (byte tick = 0; tick < rhyShape.Order; tick++) { var level = rhyShape.LevelOfBit(tick); this.clusterLevelForTick[tick] = level; } } #endregion /// /// Bit sets the occupied by harmony. /// /// The min modality level. /// Returns value. private BitArray BitsOccupiedByHarmony(byte minModalityLevel) { var harmonicOrder = this.Body.Context.Header.System.HarmonicOrder; var bits = new BitArray(harmonicOrder); //// long number = 0L; var bars = this.SurroundingBars(3, 3); foreach (var currentBar in bars.Where(currentBar => currentBar?.HarmonicBar.HarmonicStructures != null)) { bits = currentBar.HarmonicBar.HarmonicStructures.Where(hs => hs != null).Aggregate(bits, (current, hs) => current.Or(hs.BitArray)); var bitLevel = (from bool m in bits where m select m).Count(); if (bitLevel >= minModalityLevel) { break; } } return bits; } } }